<template>
  <Drawer
    :show="true"
    width="auto"
    @update:show="(show: boolean) => !show && $emit('close')"
  >
    <DrawerContent
      :title="panelTitle"
      :closable="true"
      class="w-[64rem] max-w-[100vw] relative"
    >
      <div class="w-full flex flex-col justify-start items-start gap-y-4 pb-12">
        <div class="w-full">
          <p class="mb-2">
            <span>{{ $t("project.members.condition-name") }}</span>
          </p>
          <NInput
            v-model:value="state.title"
            type="text"
            :placeholder="displayRoleTitle(binding.role)"
          />
        </div>

        <template v-if="!isLoading">
          <div
            v-if="
              binding.role === PresetRoleType.PROJECT_QUERIER ||
              binding.role === PresetRoleType.PROJECT_EXPORTER
            "
            class="w-full"
          >
            <p class="mb-2">{{ $t("common.databases") }}</p>
            <QuerierDatabaseResourceForm
              :project-name="project.name"
              :database-resources="state.databaseResources"
              @update:condition="state.databaseResourceCondition = $event"
              @update:database-resources="state.databaseResources = $event"
            />
          </div>

          <!-- Exporter blocks -->
          <template v-if="binding.role === PresetRoleType.PROJECT_EXPORTER">
            <div class="w-full flex flex-col justify-start items-start">
              <p class="mb-2">
                {{ $t("issue.grant-request.export-rows") }}
              </p>
              <MaxRowCountSelect v-model:value="state.maxRowCount" />
            </div>
          </template>
        </template>

        <div class="w-full">
          <p class="mb-2">{{ $t("common.description") }}</p>
          <NInput
            v-model:value="state.description"
            type="textarea"
            :placeholder="$t('project.members.role-description')"
          />
        </div>

        <div class="w-full">
          <p class="mb-2">{{ $t("common.expiration") }}</p>
          <NDatePicker
            v-model:value="state.expirationTimestamp"
            style="width: 100%"
            type="datetime"
            :is-date-disabled="(date: number) => date < Date.now()"
            clearable
          />
          <span v-if="!state.expirationTimestamp" class="textinfolabel">{{
            $t("project.members.role-never-expires")
          }}</span>
        </div>

        <MembersBindingSelect
          v-model:value="state.memberList"
          :required="true"
          :allow-change-type="false"
          :include-all-users="true"
          :include-service-account="true"
        />
      </div>
      <template #footer>
        <div class="w-full flex flex-row justify-between items-center">
          <div>
            <BBButtonConfirm
              v-if="allowRemoveRole()"
              :style="'DELETE'"
              :button-text="$t('common.delete')"
              :require-confirm="true"
              @confirm="handleDeleteRole"
            />
          </div>
          <div class="flex items-center justify-end gap-x-2">
            <NButton @click="$emit('close')">{{ $t("common.cancel") }}</NButton>
            <NButton
              type="primary"
              :disabled="!allowConfirm"
              @click="handleUpdateRole"
            >
              {{ $t("common.ok") }}
            </NButton>
          </div>
        </div>
      </template>
    </DrawerContent>
  </Drawer>
</template>

<script lang="ts" setup>
import { cloneDeep, isEqual } from "lodash-es";
import { NButton, NDatePicker, NInput } from "naive-ui";
import { computed, reactive, ref, onMounted } from "vue";
import { useI18n } from "vue-i18n";
import { BBButtonConfirm } from "@/bbkit";
import MaxRowCountSelect from "@/components/Issue/panel/RequestExportPanel/MaxRowCountSelect.vue";
import QuerierDatabaseResourceForm from "@/components/Issue/panel/RequestQueryPanel/DatabaseResourceForm/index.vue";
import MembersBindingSelect from "@/components/Member/MembersBindingSelect.vue";
import { Drawer, DrawerContent } from "@/components/v2";
import {
  useDatabaseV1Store,
  useProjectIamPolicy,
  useProjectIamPolicyStore,
  pushNotification,
} from "@/store";
import type { ComposedProject, DatabaseResource } from "@/types";
import { PresetRoleType } from "@/types";
import { Expr } from "@/types/proto/google/type/expr";
import { State } from "@/types/proto/v1/common";
import type { Binding } from "@/types/proto/v1/iam_policy";
import { displayRoleTitle } from "@/utils";
import { convertFromExpr } from "@/utils/issue/cel";

const props = defineProps<{
  project: ComposedProject;
  binding: Binding;
}>();

const emit = defineEmits<{
  (event: "close"): void;
}>();

interface LocalState {
  title: string;
  description: string;
  memberList: string[];
  expirationTimestamp?: number;
  // Querier and exporter options.
  databaseResourceCondition?: string;
  databaseResources?: DatabaseResource[];
  // Exporter options.
  maxRowCount: number;
  databaseId?: string;
}

const { t } = useI18n();
const databaseStore = useDatabaseV1Store();

const state = reactive<LocalState>({
  title: "",
  description: "",
  memberList: props.binding.members,
  maxRowCount: 1000,
});
const isLoading = ref(true);
const projectResourceName = computed(() => props.project.name);
const { policy: iamPolicy } = useProjectIamPolicy(projectResourceName);

const panelTitle = computed(() => {
  return displayRoleTitle(props.binding.role);
});

const allowRemoveRole = () => {
  if (props.project.state === State.DELETED) {
    return false;
  }

  // Don't allow to remove the role if the condition is empty.
  // * No expiration time.
  if (props.binding.condition?.expression === "") {
    return false;
  }

  return true;
};

const allowConfirm = computed(() => {
  return state.memberList.length > 0;
});

onMounted(() => {
  const binding = props.binding;
  // Set the display title with the role name.
  state.title = binding.condition?.title || "";
  state.description = binding.condition?.description || "";

  if (binding.parsedExpr?.expr) {
    const conditionExpr = convertFromExpr(binding.parsedExpr.expr);
    if (conditionExpr.expiredTime) {
      state.expirationTimestamp = new Date(conditionExpr.expiredTime).getTime();
    }
    if (conditionExpr.databaseResources) {
      state.databaseResources = conditionExpr.databaseResources;
      if (binding.role === PresetRoleType.PROJECT_EXPORTER) {
        if (conditionExpr.databaseResources.length > 0) {
          const selectedDatabaseResource = conditionExpr.databaseResources[0];
          const database = databaseStore.getDatabaseByName(
            selectedDatabaseResource.databaseName
          );
          if (database) {
            state.databaseId = database.uid;
          }
        }
      }
    }
    if (conditionExpr.rowLimit) {
      state.maxRowCount = conditionExpr.rowLimit;
    }
  }

  isLoading.value = false;
});

const handleDeleteRole = async () => {
  const policy = cloneDeep(iamPolicy.value);
  policy.bindings = policy.bindings.filter(
    (binding) => !isEqual(binding, props.binding)
  );
  await useProjectIamPolicyStore().updateProjectIamPolicy(
    projectResourceName.value,
    policy
  );
  emit("close");
};

const handleUpdateRole = async () => {
  const newBinding = cloneDeep(props.binding);
  if (!newBinding.condition) {
    newBinding.condition = Expr.fromPartial({});
  }
  newBinding.condition.title = state.title;
  newBinding.condition.description = state.description;
  newBinding.members = state.memberList;

  const expression: string[] = [];
  if (state.expirationTimestamp) {
    expression.push(
      `request.time < timestamp("${new Date(
        state.expirationTimestamp
      ).toISOString()}")`
    );
  }
  if (props.binding.role === PresetRoleType.PROJECT_QUERIER) {
    if (state.databaseResourceCondition) {
      expression.push(state.databaseResourceCondition);
    }
  }
  if (props.binding.role === PresetRoleType.PROJECT_EXPORTER) {
    if (state.databaseResourceCondition) {
      expression.push(state.databaseResourceCondition);
    }
    if (state.maxRowCount) {
      expression.push(`request.row_limit <= ${state.maxRowCount}`);
    }
  }
  if (expression.length > 0) {
    newBinding.condition.expression = expression.join(" && ");
  } else {
    newBinding.condition.expression = "";
  }

  const policy = cloneDeep(iamPolicy.value);
  policy.bindings = policy.bindings.filter(
    (binding) => !isEqual(binding, props.binding)
  );
  policy.bindings.push(newBinding);

  await useProjectIamPolicyStore().updateProjectIamPolicy(
    projectResourceName.value,
    policy
  );

  pushNotification({
    module: "bytebase",
    style: "SUCCESS",
    title: t("common.updated"),
  });

  emit("close");
};
</script>
